home *** CD-ROM | disk | FTP | other *** search
- /* NCRStartScript.c */
- /*
- * NCRStartScript.c
- * Copyright © 1994 Apple Computer Inc. All rights reserved.
- */
- /* .___________________________________________________________________________________.
- | These low-level routines convert an I/O request into a form that can be handled |
- | by the NCR hardware. While this is primarily specific to the NCR chip, you should |
- | understand how the setup functions call PrepareMemoryForIO. This is a limited |
- | sample that does not handle scatter-gather I/O requests -- these will be needed |
- | in a production driver. However, they require a somewhat more complex NCR script; |
- | or, at the very least, more thought on my part. |
- .___________________________________________________________________________________.
- */
- #include "NCRDriverPrivate.h"
- #include <stddef.h>
-
- OSErr NCRSetupDoSCSIScript(
- PerRequestDataPtr perRequestDataPtr
- );
- ByteCount SetupSCSICommand(
- PerRequestDataPtr perRequestDataPtr
- );
- ByteCount SCSIGetCommandLength(
- const unsigned char *cmdBlock
- );
- OSErr NCRSetupTestISR(
- PerRequestDataPtr perRequestDataPtr
- );
- OSErr NCRSetupTestMemory(
- PerRequestDataPtr perRequestDataPtr
- );
-
- /*
- * All functions here use a standard variable set to access information.
- * I've "globalized" this to minimize clutter.
- */
- #define REQUEST (*perRequestDataPtr)
- #define PB (*((IOParam *) REQUEST.pb))
- #define SCSI (*((NCRSCSIParamPtr) PB.ioMisc))
- #define SCRIPT (REQUEST.scriptData)
- /*
- * AddressOf is a physical address
- */
- #define AddressOf(what) (((UInt32) REQUEST.scriptDataPtr) + offsetof(ScriptData, what))
- /*
- * SetTable (must be a macro) stores an address and byte count into a table.
- */
- #define SetTable(whichTable, physAddress, dataCount) do { \
- SCRIPT.whichTable.address = EndianSwap32Bit((UInt32) physAddress); \
- SCRIPT.whichTable.byteCount = EndianSwap32Bit(dataCount); \
- } while (0)
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * NCRStartScript starts a Script operation. The script completes through a Secondary
- * Interrupt Handler.
- */
- OSErr
- NCRStartScript(
- AddressSpaceID addressSpaceID,
- IOCommandID ioCommandID,
- ParmBlkPtr pb,
- ScriptSelector scriptSelector
- )
- {
- OSErr status;
- PerRequestDataPtr perRequestDataPtr;
-
- Trace(NCRStartScript);
- /*
- * When we support concurrent transactions, we'll get the current
- * PerRequestData record from a pool. If there aren't any available,
- * we'll hide this parameter block on a look-aside queue and return
- * "in progress".
- */
- perRequestDataPtr = GLOBAL.perRequestDataPtr;
- /*
- * Clear out the volatile parts of the table.
- */
- CLEAR(REQUEST.scriptData); /* Shared with the NCR chip */
- CLEAR(REQUEST.shadow); /* Current interrupt state */
- REQUEST.timerID = kInvalidID; /* No watchdog timer id yet */
- REQUEST.addressSpaceID = addressSpaceID;
- REQUEST.pb = pb;
- REQUEST.ioCommandID = ioCommandID;
- if (scriptSelector == kSCSICommandScript
- && SCSI.targetID == kNCRMemoryTestBusID) {
- scriptSelector = (SCSI.memTestPhysAddress == NULL)
- ? kSCSITestISRScript : kSCSITestMemoryScript;
- }
- REQUEST.scriptSelector = scriptSelector;
- switch (scriptSelector) {
- case kBusResetScript:
- REQUEST.scriptPtr =
- ((UInt32) GLOBAL.scriptIOTable.physicalMapping[0])
- + kBusResetScript;
- REQUEST.scriptBaseAddress =
- (UInt32) GLOBAL.scriptIOTable.physicalMapping[0];
- REQUEST.scriptDataPtr = kInvalidPageAddress;
- REQUEST.watchdogTimeout = kNoSCSITimeout; /* We have a standard timeout */
- status = noErr;
- break;
- case kSCSICommandScript:
- REQUEST.watchdogTimeout = SCSI.watchdogTimeout;
- REQUEST.scriptPtr =
- ((UInt32) GLOBAL.scriptIOTable.physicalMapping[0])
- + kSCSICommandScript;
- REQUEST.scriptDataPtr =
- ((UInt32) REQUEST.perRequestIOTable.physicalMapping[0])
- + offsetof(PerRequestData, scriptData);
- status = NCRSetupDoSCSIScript(perRequestDataPtr);
- REQUEST.scriptBaseAddress =
- (UInt32) GLOBAL.scriptIOTable.physicalMapping[0];
- break;
- case kSCSIRundownScript:
- status = NCRBusBusyCheck();
- if (status == noErr) {
- REQUEST.watchdogTimeout =
- ((NCRDriverRundownParamPtr) pb)->watchdogTimeout;
- REQUEST.scriptPtr =
- ((UInt32) GLOBAL.scriptIOTable.physicalMapping[0])
- + kSCSIRundownScript;
- REQUEST.scriptDataPtr =
- ((UInt32) REQUEST.perRequestIOTable.physicalMapping[0])
- + offsetof(PerRequestData, scriptData);
- REQUEST.scriptBaseAddress =
- (UInt32) GLOBAL.scriptIOTable.physicalMapping[0];
- SCRIPT.idMsgByte = 0x0; /* Bogus id */
- SCRIPT.statusByte = 0xFF; /* Initialize to garbage */
- SCRIPT.commandCompleteByte = 0xFF; /* Initialize to garbage */
- /*
- * Setup the scatter-gather tables.
- */
- SetTable(deviceIDTable, 0, 0);
- SetTable(idMsgTable, AddressOf(idMsgByte), 1);
- SetTable(commandTable, AddressOf(scsiCommand), 0);
- SetTable(statusTable, AddressOf(statusByte), 1);
- SetTable(completeTable, AddressOf(commandCompleteByte), 1);
- SetTable(bitBucketInTable, AddressOf(bitBucketInByte), 1);
- SetTable(bitBucketOutTable, AddressOf(bitBucketOutByte), 1);
- SetTable(ignoredMsgTable, AddressOf(ignoredMsgInByte), 1);
- }
- break;
- case kSCSITestISRScript:
- REQUEST.watchdogTimeout = SCSI.watchdogTimeout;
- REQUEST.scriptDataPtr = kInvalidPageAddress;
- status = NCRSetupTestISR(perRequestDataPtr);
- break;
- case kSCSITestMemoryScript:
- REQUEST.watchdogTimeout = SCSI.watchdogTimeout;
- REQUEST.scriptDataPtr = kInvalidPageAddress;
- status = NCRSetupTestMemory(perRequestDataPtr);
- break;
- default:
- status = paramErr; /* Can't happen */
- break;
- }
- if (status == noErr) {
- /*
- * We are ready to start an NCR I/O operation. The actual work is
- * done by the Secondary Interrupt Handler.
- */
- status = NCRQueueSecondaryInterrupt(perRequestDataPtr, kIORequestStart);
- if (status == noErr) /* If successful, we return */
- status = kIOBusyStatus; /* busy to the driver mainline */
- }
- if (status != kIOBusyStatus) {
- /*
- * We can't start I/O -- make sure the user's buffer is un-prepared.
- */
- CheckStatus(status, "\pNCRStartScript");
- CheckpointIOTable(&REQUEST.scsiIOTable);
- }
- return (status);
- }
-
- /*
- * Setup a SCSI request. pb->ioMisc points to the NCRSCSIParam structure.
- */
- OSErr
- NCRSetupDoSCSIScript(
- PerRequestDataPtr perRequestDataPtr
- )
- {
- OSErr status;
- ByteCount scsiCommandLength;
- UInt32 ncrDeviceID;
-
- Trace(NCRSetupDoSCSIScript);
- /*
- * Check for valid parameters. The caller has already verified that PBRead
- * or PBWrite matches SCSI.driverAction.
- */
- status = noErr;
- PB.ioActCount = 0;
- #if 0 && USE_LOG_LIBRARY
- WriteLogEntry(GLOBAL.logRecordPtr, 'StSc',
- LogFormat7(
- kLogFormatAddress,
- kLogFormatSigned, kLogFormatSigned, kLogFormatSigned,
- kLogFormatAddress, kLogFormatUnsigned,
- kLogFormatAddress
- ),
- &SCSI,
- (signed long) SCSI.targetID,
- (signed long) SCSI.logicalUnitNumber,
- (signed long) SCSI.driverAction,
- PB.ioBuffer, PB.ioReqCount,
- &SCSI.logicalUnitNumber
- );
- #endif
- if (SCSI.logicalUnitNumber != 0) /* This should be ok now */
- status = scsiLUNInvalid; /* LUN's are unsupported */
- else if (SCSI.targetID == GLOBAL.initiatorID || SCSI.targetID >= 8)
- status = scsiTIDInvalid; /* Invalid target ID */
- else if ((PB.ioReqCount & 0xFF000000) != 0) /* Long transfer length */
- status = scsiRequestInvalid; /* Longer than SCSI Max */
- else if (PB.ioBuffer != NULL && PB.ioReqCount == 0) {
- status = scsiRequestInvalid;
- }
- else if (SCSI.driverAction != kNCRDriverNoDataPhase && PB.ioBuffer == NULL) {
- status = paramErr;
- }
- if (status == noErr) {
- scsiCommandLength = SetupSCSICommand(perRequestDataPtr);
- if (scsiCommandLength == 0) /* Invalid SCSI command? */
- status = paramErr;
- }
- /*
- * Here is where we prepare the SCSI and per-request tables for the
- * next request. PrepareNewDMATransfer understands "no data" transfers.
- */
- if (status == noErr)
- status = PrepareNewDMATransfer(perRequestDataPtr);
- if (status == noErr) {
- /*
- * We now have physical addresses for all data, Bind the data area
- * to the script Table elements. All SCSI bus to/from memory
- * accesses are done using the MoveFrom script operator and a
- * static table. This is rather ugly, but the alternative would
- * require patching physical addresses into the script. As done
- * here, the entire script is read-only (after the first call that
- * resolves our hack labels).
- *
- * Note that all addresses that will be read by the NCR chip must
- * be byte-swapped. (Hmm, strictly speaking, this is not quite
- * correct: we can jumper pin 142 (BIT_LIT/) to set the chip into
- * big-endian mode. By running the chip in "PCI-native"
- * little-endian mode, we illustrate the byte-swap requirements.
- */
- ncrDeviceID = (ByteCount)
- ( (0 << 24) /* bytelane 3 SCNTL3 config bits */
- | (SCSI.targetID << 16) /* bytelane 2 Target ID */
- | (0 << 8) /* bytelane 1 SFXFER Offset/Period */
- | 0 /* bytelane 0 Must be zero */
- );
- SCRIPT.idMsgByte = 0x80 /* Disable disconnect */
- | (SCSI.logicalUnitNumber & 0x07); /* Stuff in the LUN */
- SCSI.statusByte = SCRIPT.statusByte = 0xFF;
- SCSI.messageByte = SCRIPT.commandCompleteByte = 0xFF;
- SCRIPT.bitBucketOutByte = kScsiMsgAbort; /* For MSGO phase */
- /*
- * Setup the scatter-gather tables.
- */
- SetTable(deviceIDTable, 0, ncrDeviceID);
- SetTable(idMsgTable, AddressOf(idMsgByte), 1);
- SetTable(commandTable, AddressOf(scsiCommand), scsiCommandLength);
- SetTable(statusTable, AddressOf(statusByte), 1);
- SetTable(completeTable, AddressOf(commandCompleteByte), 1);
- SetTable(bitBucketInTable, AddressOf(bitBucketInByte), 1);
- SetTable(bitBucketOutTable, AddressOf(bitBucketOutByte), 1);
- SetTable(ignoredMsgTable, AddressOf(ignoredMsgInByte), 1);
- }
- CheckStatus(status, "\pNCRSetupDoSCSIScript failure");
- return (status);
- }
-
- /*
- * SetupSCSIComand determines the command length and copies the command to the
- * per-request record. It returns 0 if the command length is incorrect.
- */
- ByteCount
- SetupSCSICommand(
- PerRequestDataPtr perRequestDataPtr
- )
- {
- ByteCount scsiCommandLength;
-
- Trace(SetupSCSICommand);
- /*
- * We will copy data from the parameter block into the per-request
- * data area. While this simplifies debugging, it has an additional
- * advantage: it cuts down on the number of PrepareMemoryForIO
- * requests to the minimum (script, per-request table, and user data).
- * The only limitation is that it prevents our device from working
- * with a non-standard SCSI device whose command block is longer than
- * 12 bytes.
- */
- scsiCommandLength = SCSI.scsiCommandLength;
- if (scsiCommandLength == 0)
- scsiCommandLength = SCSIGetCommandLength(SCSI.scsiCommand);
- if (scsiCommandLength == 0
- || scsiCommandLength > sizeof SCRIPT.scsiCommand)
- scsiCommandLength = 0;
- if (scsiCommandLength > 0) {
- BlockCopy(SCSI.scsiCommand, SCRIPT.scsiCommand, scsiCommandLength);
- /*
- * Store the LUN information in the command block - this is needed
- * for old devices that only examine the command block for LUN values.
- * (On SCSI-II, the the LUN is stored in the identify message).
- */
- SCRIPT.scsiCommand[1] &= ~0xE0;
- SCRIPT.scsiCommand[1] |= (SCSI.logicalUnitNumber & 0x07) << 5;
- #if 1 && USE_LOG_LIBRARY
- {
- Str255 work;
- int i;
-
- work[0] = 0;
- AppendSigned(work, SCSI.targetID);
- AppendChar(work, ' ');
- AppendUnsigned(work, /* Try to capture the block number */
- ( (SCRIPT.scsiCommand[2] << 16)
- + (SCRIPT.scsiCommand[3] << 8)
- + (SCRIPT.scsiCommand[4] << 0)
- )
- );
- AppendChar(work, ' ');
- AppendUnsigned(work, PB.ioReqCount);
- AppendChar(work, ' ');
- for (i = 0; i < scsiCommandLength; i++) {
- AppendHexLeadingZeros(work, SCRIPT.scsiCommand[i], 2);
- if (i < (scsiCommandLength - 1))
- AppendChar(work, '.');
- }
- WriteLogEntry(GLOBAL.logRecordPtr, 'SCSI',
- LogStringFormat, work);
- }
- #endif
- }
- return (scsiCommandLength);
- }
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * SCSIGetCommandLength looks at the first command byte. If it's in the ANSI-standard
- * range, return the command length.
- */
- ByteCount
- SCSIGetCommandLength(
- const unsigned char *cmdBlock
- )
- {
- ByteCount result;
-
- Trace(SCSIGetCommandLength);
- /*
- * Look at the "group code" in the command operation. Return a parameter
- * error for the reserved (3, 4) and vendor-specific command (6, 7)
- * command groups. Otherwise, set the command length from the group code
- * value as specified in the SCSI-II spec. Then, copy the command block
- * into the parameter block (this centralizes everything for debugging
- * convenience).
- */
- switch (cmdBlock[0] & 0xE0) {
- case (0 << 5): result = 6; break;
- case (1 << 5):
- case (2 << 5): result = 10; break;
- case (5 << 5): result = 12; break;
- default: result = 0; break; /* This results in an error */
- }
- return (result);
- }
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Setup the ISR test: create a one-instruction script "INT noErr" and return success.
- */
- OSErr
- NCRSetupTestISR(
- PerRequestDataPtr perRequestDataPtr
- )
- {
- Trace(NCRSetupTestISR);
- PB.ioActCount = 0;
- CLEAR(REQUEST.memoryMoveScript);
- REQUEST.memoryMoveScript[0] = kIntOpcode;
- REQUEST.memoryMoveScript[1] = noErr;
- REQUEST.scriptPtr =
- ((UInt32) REQUEST.perRequestIOTable.physicalMapping[0])
- + offsetof(PerRequestData, memoryMoveScript);
- REQUEST.scriptBaseAddress = REQUEST.scriptPtr;
- return (noErr);
- }
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Setup the memory test: Make sure we have the necessary buffers, setup the memory
- * move script, then PrepareMemoryForIO.
- */
- OSErr
- NCRSetupTestMemory(
- PerRequestDataPtr perRequestDataPtr
- )
- {
- OSErr status;
-
- Trace(NCRSetupTestMemory);
- PB.ioActCount = 0;
- status = noErr;
- if (PB.ioBuffer == NULL || SCSI.memTestPhysAddress == NULL)
- status = paramErr;
- if (status == noErr)
- status = PrepareNewDMATransfer(perRequestDataPtr);
- if (status == noErr)
- status = CheckForContiguousPhysicalMapping(&REQUEST.scsiIOTable);
- if (status == noErr) {
- REQUEST.scriptPtr =
- ((UInt32) REQUEST.perRequestIOTable.physicalMapping[0])
- + offsetof(PerRequestData, memoryMoveScript);
- REQUEST.memoryMoveScript[3] = kIntOpcode;
- REQUEST.memoryMoveScript[4] = noErr; /* noErr */
- REQUEST.scriptBaseAddress = REQUEST.scriptPtr;
- }
- CheckStatus(status, "\pNCRSetupTestMemory");
- return (status);
- }
-
-